import React, { Dispatch, ReactNode, SetStateAction, useEffect, useState } from 'react';

import { setAgentLeave, setAgentStreamList } from '@/store/reducers/stream';
import useSyncState from '../hooks/useSyncState';
import { useBusiness, BusinessTaskMap, IBusinessStatus } from './BusinessContext';
import message from '@/utils/message';
import backgroundEffect from '@/utils/BackgroundEffect';
import AgentService from '@/modules/Agent';
import { IOperationName } from '@/modules/interface';
import { cancelInvitations } from '@/utils/lib';

export interface IStream {
	id: string | undefined;
	label: string;
	audioOnly: boolean;
	clientId: string;
	videoId: string;
	muteStatus: boolean;
	streamMode: 'audio' | 'video';
	role: string;
	isScreenSyncMode: boolean;
	userName: string;
	isRobot?: boolean;
}

export interface IRecordPoint {
	recordingId: string;
	content: string;
	timePoint: number;
	userId: number | string;
	userName: string;
	userRole: string;
}

const initalRecordPoint = {
	recordingId: '',
	content: '',
	timePoint: 0,
	userId: 0,
	userName: '',
	userRole: ''
};

const StreamContext = React.createContext<
	| {
			streams: () => IStream[];
			streamAdd: (stream: IStream) => void;
			videoInput: () => string | undefined;
			audioOutput: () => string | undefined;
			audioInput: () => string | undefined;
			setVideoInput: (id: string | undefined) => void;
			setAudioOutput: (id: string | undefined) => void;
			setAudioInput: (id: string | undefined) => void;
			streamEnd: (clientId: string) => void;
			screenStream: () => IStream[];
			smallStream: () => IStream[];
			largeStream: () => IStream[];
			clearStreams: () => void;
			setStreams: (streams: IStream[]) => void;
			getAudioLevel: (videoId: string, callback: (vol: number) => void) => void;
			resumeGetAudioLevel: (videoId: string) => void;
			suspendGetAudioLevel: (videoId: string) => void;
			recordPoint: () => IRecordPoint;
			setRecordPoint: (data: IRecordPoint) => void;
			boardCastStream: () => IStream[];
			setBoardCastStream: (boardCastStream: IStream[]) => void;
			renderTTSStream: () => any[];
			setRenderTTSStream: (renderTTSStream: any) => void;
			audioStreams: () => IStream[];
			setAudioStreams: (audioStreams: IStream[]) => void;
			setSmallStream: (stream: IStream[]) => void;
			cancelInvite: () => void;
			updateStream: (stream: IStream) => void;
	  }
	| undefined
>(undefined);
StreamContext.displayName = 'StreamProvider';

const StreamProvider = ({ children }: { children: ReactNode }) => {
	const { setBusiness, business, notifyOtherAgents, customerMembers } = useBusiness();
	const [streams, setStreams] = useSyncState<IStream[]>([]);
	const [agentStream, setAgentStream] = useSyncState<IStream[]>([]);
	const [screenStream, setScreenStream] = useSyncState<IStream[]>([]);
	const [smallStream, setSmallStream] = useSyncState<IStream[]>([]);
	const [largeStream, setLargeStream] = useSyncState<IStream[]>([]);
	const [customerStream, setCustomerStream] = useSyncState<IStream[]>([]);
	const [recordPoint, setRecordPoint] = useSyncState<IRecordPoint>(initalRecordPoint);
	//TTS
	const [boardCastStream, setBoardCastStream] = useSyncState<IStream[]>([]);
	const [renderTTSStream, setRenderTTSStream] = useSyncState<any>([]);
	const [audioStreams, setAudioStreams] = useSyncState<IStream[]>([]);

	const [videoInput, setVideoInput] = useSyncState<string | undefined>(AgentService.getVideoDeviceID());
	const [audioOutput, setAudioOutput] = useSyncState<string | undefined>(AgentService.getSpeakerDeviceID() ? AgentService.getSpeakerDeviceID() : AgentService.getDefaultSpeaker());
	const [audioInput, setAudioInput] = useSyncState<string | undefined>(AgentService.getAudioDeviceID() ? AgentService.getAudioDeviceID() : AgentService.getDefaultAudioInputDeviceID());

	const cancelInvite = async () => {
		try {
			const data = await cancelInvitations();
			console.log('cancelInvitations success', data);
		} catch (error) {
			console.log('cancelInvitations error', error);
		}
	};

	const replaceInviteStream = (stream: IStream) => {
		const inviteStreamIndex = streams().findIndex(item => item.id?.includes(IOperationName.INVITE));
		let newStreams = [...streams()];
		if (stream.userName && newStreams.find(item => item.id?.includes(stream.userName) || item.id === 'invite*')) {
			newStreams[inviteStreamIndex] = stream;
			AgentService.removeOperationWithId(IOperationName.INVITE);
			if (AgentService.sessionMembers.find(item => item.user.name === stream.userName)?.type === 'force') {
				cancelInvite();
			}
		} else {
			if (!streams().find(item => item.videoId === stream.videoId)) {
				newStreams = [...streams(), stream];
			}
		}

		console.log(newStreams, stream, 'replaceInviteStream');

		return newStreams;
	};

	/** 新的流加入 */
	const streamAdd = (stream: IStream) => {
		setStreams(replaceInviteStream(stream));

		if (stream.label.indexOf('agent') > -1) {
			const tasks = [...BusinessTaskMap.BusinessQuene.values()];
			console.log('streamAdd:', stream, tasks);
			tasks.forEach(item => {
				const receiver = AgentService.getCustomerMediaStream().find(ele => ele.clientId === item.clientId);
				setTimeout(() => {
					console.log('notifyOtherAgents:', receiver);
					receiver && notifyOtherAgents(item.name, item.status, receiver);
				}, 500);
			});
		}
		setBusiness([...business]);
	};

	/** 旧的流离开 */
	const streamEnd = (clientId: string) => {
		console.log('streamEnd:', clientId);
		const streamList = streams().filter((item: IStream) => item.clientId !== clientId);
		BusinessTaskMap.delComletedTask(clientId);
		setBusiness([...business]);
		setStreams(streamList);
	};

	/** 筛选流操作 */
	const findLargeStream = () => {
		const stream = streams().find(item => item.videoId.indexOf('customer') > -1);
		if (stream) {
			setLargeStream([stream]);
		} else {
			setLargeStream([]);
		}
	};

	const updateStream = (stream: IStream) => {
		setStreams(
			streams().map(item => {
				if (item.videoId === stream.videoId) {
					return { ...item, ...stream };
				}
				return item;
			})
		);

		console.log(streams(), 'updateStream');
	};

	const findSmallStream = () => {
		const smallStreams = streams().filter(
			item => item.clientId !== largeStream()[0]?.clientId && item.label !== 'recordScreen' && item.label.indexOf('screen') <= -1 && item.label !== 'recordScreen-Regional'
		);

		if (smallStreams.length > 0) {
			setSmallStream(smallStreams);
		} else {
			setSmallStream([]);
		}
	};

	const findScreenStream = () => {
		const screenStreams = streams().filter(item => item.label.indexOf('screen') > -1);
		if (screenStreams.length > 0) {
			setScreenStream(screenStreams);
		} else {
			setScreenStream([]);
		}
	};

	/** 监听 audioLevel */
	const getAudioLevel = (videoId: string, callback: (volume: number) => void) => {
		ivcs.agent.getAudioLevel(
			videoId,
			success => {
				console.log('getAudioLevel:', success, videoId);
			},
			error => {
				console.log('getAudioLevel:', error, videoId);
			},
			volume => {
				callback && callback(volume);
			}
		);
	};

	/** 暂停 audioLevel 监听 */
	const suspendGetAudioLevel = (videoId: string) => {
		ivcs.agent.suspendGetAudioLevel(
			videoId,
			success => {
				console.log('suspendGetAudioLevel:', success);
			},
			error => {
				console.log('suspendGetAudioLevel:', error);
			}
		);
	};

	/** 恢复 audioLevel 监听  */
	const resumeGetAudioLevel = (videoId: string) => {
		ivcs.agent.resumeGetAudioLevel(
			videoId,
			success => {
				console.log('suspendGetAudioLevel:', success);
			},
			error => {
				console.log('suspendGetAudioLevel:', error);
			}
		);
	};

	/** 清空所有流数据 */
	const clearStreams = () => {
		setStreams([]);
		console.log(streams());
		backgroundEffect.stopEffect(true);
	};

	/** 监听流变化，筛选流 */
	useEffect(() => {
		findLargeStream();
		findSmallStream();
		findScreenStream();
	}, [streams()]);
	return (
		<StreamContext.Provider
			children={children}
			value={{
				streams,
				streamAdd,
				streamEnd,
				screenStream,
				setStreams,
				largeStream,
				smallStream,
				clearStreams,
				getAudioLevel,
				resumeGetAudioLevel,
				suspendGetAudioLevel,
				recordPoint,
				setRecordPoint,
				boardCastStream,
				setBoardCastStream,
				renderTTSStream,
				setRenderTTSStream,
				audioStreams,
				setAudioStreams,
				videoInput,
				audioOutput,
				audioInput,
				setVideoInput,
				setAudioOutput,
				setAudioInput,
				setSmallStream,
				cancelInvite,
				updateStream
			}}
		></StreamContext.Provider>
	);
};

const useStream = () => {
	const context = React.useContext(StreamContext);
	if (!context) {
		throw new Error('useStream 必须在 AppProvider 中使用');
	}
	return context;
};

export { useStream, StreamProvider };
